<?php

namespace App\Services\Bolai;

use App\Model\ChargeOption;
use App\Model\Device;
use App\Model\Normal;
use App\Model\Order;
use Carbon\Carbon;
use GuzzleHttp\Client;
use Hyperf\Di\Annotation\Inject;

use Hyperf\Guzzle\CoroutineHandler;
use Hyperf\Guzzle\HandlerStackFactory;
use Hyperf\Utils\ApplicationContext;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\Log\LoggerInterface;
use Swoole\Server;

/**
 * 打包类
 */
class PackService
{


    #[Inject]
    protected Device $device;

    #[inject]
    protected BlService $blService;

    private $bkvData;

    private $order;


    public function __construct()
    {

    }


    /**按时间开锁
     * @return string
     */
    public function startTimePort($device,$port = 0, $time = 10)
    {
        try {
            $b = new BKV();
            $b->addByNumberKey(0x01, 0x1007);
            $b->addByNumberKey(0x02, $this->generateRequestId());
            $b->addByNumberKey(0x03, (int)$device->mac);
            $b->addByNumberKey(0x08, (int)$port);
            $b->addByNumberKey(0x47, 1);//扫码
            $b->addByNumberKey(0x13, 1);
            //下方为可变参数
            $b->addByNumberKey(0x12, 1);
            $b->addByNumberKey(0x14, $time,2);
            //下方为可变公用参数
            $b->addByNumberKey(0x21, 3600,2);
            $b->addByNumberKey(0x22, 60,2);
            $b->addByNumberKey(0x23, 200,2);
            $b->addByNumberKey(0x24, 200,2);
            $result = $b->pack();
            $message = $this->blService::buildFrame($b->bufferToHex($result));
            $this->blService->sendToDevice($device->fd, $message);
            return $message;
        } catch (\Exception $e) {
            return $e->getMessage();
        }
    }

    public function send(): void
    {
        $result = $this->bkvData->pack();
        $message = $this->blService::buildFrame($this->bkvData->bufferToHex($result));
        $data['fd'] = $this->order->device->fd;
        $data['message'] = $message;
//        $this->order->use_detail = $this->bkvData->bufferToHex($result);
        $this->order->save();
  //      $this->blService->sendToDevice($this->order->device->fd, $message);
        try{
            $this->post_curl('http://123.156.230.117:9005/tcp/query_device',$data);
        }catch (\Exception $e){
            Normal::logger('order-send--message---error---' . $e->getMessage() . $e->getTraceAsString(), 'notify-event');
        }

    }


    public function post_curl($url, $data,$type = 'form_params',$charset='UTF-8')
    {
        $container = ApplicationContext::getContainer();
        $handlerStackFactory = $container->get(HandlerStackFactory::class);
        $handlerStack = $handlerStackFactory->create();
        $client = new Client(['handler' => new CoroutineHandler($handlerStack)]);
        $response = $client->post($url, [
            'headers' => [
                'Content-Type' => 'application/x-www-form-urlencoded;charset='.$charset,
            ],
            $type => $data
        ]);

        return $response->getBody()->getContents();
    }

    public function setCommon(Order $order): object
    {
        $this->order = $order;
        $this->bkvData = new BKV();
        $requestId = $this->generateRequestId();
        $this->order->mac =(int)$this->order->device->mac;
        $this->bkvData->addByNumberKey(0x01, 0x1007);
        $this->bkvData->addByNumberKey(0x02, $requestId);
        $this->bkvData->addByNumberKey(0x03, $this->bkvData->handleMacId((int)$this->order->device->mac));
        $this->bkvData->addByNumberKey(0x08, (int)$this->order->channel_number);
        $this->bkvData->addByNumberKey(0x47, 1);//扫码
        //footer
//        $this->bkvData->addByNumberKey(0x21, 3600, 2);
//        $this->bkvData->addByNumberKey(0x22, 60, 2);
//        $this->bkvData->addByNumberKey(0x23, 10, 2);
//        $this->bkvData->addByNumberKey(0x24, 10, 2);
        return $this;
    }

    public function end():void{
        $this->bkvData->addByNumberKey(0x13, 0);
        $this->send();
    }


    /**按功率
     * @param $device
     * @param $port
     * @param $fee
     * @return string
     */
    public function power(): void
    {
        $power = $this->generatePowerChargeInfo($this->order->charge->power_children);
        $this->bkvData->addByNumberKey(0x12, 3, 1);//新版为5
        $this->bkvData->addByNumberKey(0x13, 1);
        $this->bkvData->addByNumberKey(0x2b, (int)($this->order->power_fee * 100), 2);
        $this->bkvData->addByNumberKey(0x2c, count($this->order->charge->power_children));
        $this->bkvData->addByNumberKey(0x2d, $power, 6 * count($this->order->charge->power_children));
        $this->order->mode = 3;
        $this->order->save();
        $this->send();
    }

    /**按服务费
     * @param $device
     * @param $port
     * @param $fee
     * @return string
     */
    public function service(): void
    {
        //功率计费参数拼接
        $this->bkvData->addByNumberKey(0x12, 4);//新版为6
        $this->bkvData->addByNumberKey(0x13, 1);
        $this->bkvData->addByNumberKey(0x88, (int)($this->order->power_fee * 100), 2);
        $this->bkvData->addByNumberKey(0x81, 0, 2);//服务费起步价
        $this->bkvData->addByNumberKey(0x82, 10000, 2);//服务费上限
        //
        $this->bkvData->addByNumberKey(0x89, 1);
//        $times = $this->convertMinutesToHoursMinutes(0);

        if ($this->order->charge->service_type == 2) {
            $chargeInfo[0]['hours'] = 23;
            $chargeInfo[0]['minutes'] = 59;
            $chargeInfo[0]['elec_price'] = round($this->order->charge->elec_price * 100);
            $chargeInfo[0]['service_fee'] = 0;
            $this->bkvData->addByNumberKey(0x83, $this->generateServiceChargeInfo($chargeInfo), 6);
            $this->bkvData->addByNumberKey(0x2c,count($this->order->charge->service_children));
            $chargeInfo = $this->parseServiceChildren($this->order->charge->service_children);
            $this->bkvData->addByNumberKey(0x2d,$chargeInfo,6*count($this->order->charge->service_children));
            $this->bkvData->addByNumberKey(0x80, 2);
        }else{
            $chargeInfo[0]['hours'] = 23;
            $chargeInfo[0]['minutes'] = 59;
            $chargeInfo[0]['elec_price'] = round($this->order->charge->elec_price * 100);
            $chargeInfo[0]['service_fee'] = round($this->order->charge->service_fee * 100);
            $this->bkvData->addByNumberKey(0x83, $this->generateServiceChargeInfo($chargeInfo), 6);
            $this->bkvData->addByNumberKey(0x80, 1);//0---按时收取1---按电量收取2---按充电功率区间每档加和收取3---按充电功率区间最大值收取【2，3使用时，需要下发0x2c,0x2d】
        }
        $this->order->mode = 4;
        $this->order->save();
        $this->send();
    }


    public function parseServiceChildren($serviceChildren): string
    {
        $packedData = '';
        foreach ($serviceChildren as $level) {
            // 打包每档信息：功率 + 金额标准 + 时间标准
            $packedData .= pack('nnn', $level->max_power, round($level->fee*100), 60);
        }
        return $packedData;
    }

    /**服务费套餐参数转换
     * @param $charge
     * @return string
     */
    public function generateServiceChargeInfo($charge): string
    {
        $result = '';
        foreach ($charge as $level) {
            // 时间：1字节小时 + 1字节分钟
            $time = pack('CC', $level['hours'], $level['minutes']);
            // 电费单价：2字节
            $electricityFee = pack('n', $level['elec_price']);
            // 服务费单价：2字节
            $serviceFee = pack('n', $level['service_fee']);
            // 拼接每档信息
            $result .= $time . $electricityFee . $serviceFee;
        }
        return $result;
    }


    public function convertMinutesToHoursMinutes(int $totalMinutes): array
    {
        // 计算小时数
        $data['hours'] = intdiv($totalMinutes, 60);
        // 计算剩余分钟数
        $data['minutes'] = $totalMinutes % 60;

        // 格式化输出
        return $data;
    }


    public function generatePowerChargeInfo($charge): string
    {
        $result = '';
        foreach ($charge as $level) {
            // 每档信息：功率（2字节） + 金额标准（2字节） + 时间标准（2字节）
            $result .= pack('n3', $level['max_power'], 1 * 100, $level['minutes']);
        }
        return $result;
    }


    public function startElecPort($device, $port)
    {
        $b = new BKV();
        $b->addByNumberKey(0x01, 0x1007);
        $b->addByNumberKey(0x02, $this->generateRequestId());
        $b->addByNumberKey(0x03, $b->handleMacId((int)$device->mac));
        $b->addByNumberKey(0x08, (int)$port);
        $b->addByNumberKey(0x47, 1);//扫码
        $b->addByNumberKey(0x13, 1);
    }



    /**
     * 查询设备参数0x100c（平台下发）
     * @param Device $device
     * @return string
     */
    public function selectDeviceParams(Device $device): string
    {
        try {
            $b = new BKV();
            $b->addByNumberKey(0x01, 0x100c, 2);
            $b->addByNumberKey(0x02, $this->generateRequestId());
            $b->addByNumberKey(0x03, $b->handleMacId($device->mac), 8);
            $result = $b->pack();
            $message = $this->blService::buildFrame($b->bufferToHex($result));
            $data['fd'] = $device->fd;
            $data['message'] = $message;
            $this->post_curl('http://123.156.230.117:9005/tcp/query_device',$data);
            return $message;
        } catch (\Exception $e) {
            Normal::logger('selectDeviceParams-send--message---error---' . $e->getMessage() . $e->getTraceAsString(), 'notify-event');
            return $e->getMessage();
        }
    }

    /**
     * 查询设备状态0x1003（平台下发）
     * @param Device $device
     * @return string
     */
    public function selectDeviceStatus(Device $device): string
    {
        try {
            $b = new BKV();
            $b->addByNumberKey(0x01, 0x1003, 2);
            $b->addByNumberKey(0x02, $this->generateRequestId());
            $b->addByNumberKey(0x03, $b->handleMacId($device->mac), 8);
            $result = $b->pack();
            $message = $this->blService::buildFrame($b->bufferToHex($result));
            $data['fd'] = $device->fd;
            $data['message'] = $message;
            $this->post_curl('http://123.156.230.117:9005/tcp/query_device',$data);
            return $message;
        } catch (\Exception $e) {
            Normal::logger('selectDeviceStatus-send--message---error---' . $e->getMessage() . $e->getTraceAsString(), 'notify-event');
            return $e->getMessage();
        }
    }

    /**
     * 设置系统参数0x100b（平台下发）
     * @param $fd
     * @param $mac
     * @param array $data
     * @return string
     */
    public function setDeviceParams($fd,$mac,array $data): string
    {
        try {
            $b = new BKV();
            $b->addByNumberKey(0x01, 0x100b, 2);
            $b->addByNumberKey(0x02, $this->generateRequestId());
            $b->addByNumberKey(0x03, $b->handleMacId($mac), 8);
            if ($data['report_cycle']) $b->addByNumberKey(0x1e, (int)$data['report_cycle'], 2);
            if ($data['heart_cycle']) $b->addByNumberKey(0x20, (int)$data['heart_cycle'], 2);
            if ($data['full_charge_delay']) $b->addByNumberKey(0x21, (int)$data['full_charge_delay'], 2);
            if ($data['null_charege_delay']) $b->addByNumberKey(0x22, (int)$data['null_charege_delay'], 2);
            if ($data['full_charge_power']) $b->addByNumberKey(0x23, (int)$data['full_charge_power'], 2);
            if ($data['null_charge_power']) $b->addByNumberKey(0x24, (int)$data['null_charge_power'], 2);
            if ($data['temp_th']) $b->addByNumberKey(0x25, (int)$data['temp_th'], 1);
            if ($data['max_charge_time']) $b->addByNumberKey(0x59, (int)$data['max_charge_time'], 2);
            if ($data['threshold_i']) $b->addByNumberKey(0x10, (int)$data['threshold_i'], 2);
            if ($data['threshold_p']) $b->addByNumberKey(0x11, (int)$data['threshold_p'], 2);
            if ($data['charge_single_amount']) $b->addByNumberKey(0x68, (int)$data['charge_single_amount'], 4);
            $result = $b->pack();
            $message = $this->blService::buildFrame($b->bufferToHex($result));
            $data['fd'] = $fd;
            $data['message'] = $message;
            $this->post_curl('http://123.156.230.117:9005/tcp/query_device',$data);
            return $message;
        } catch (\Exception $e) {
            Normal::logger('setDeviceParams-send--message---error---' . $e->getMessage() . $e->getTraceAsString(), 'notify-event');
            return $e->getMessage();
        }
    }

    /**
     * 系统重启0x100d（平台下发）
     * @param $fd
     * @param $mac
     * @param int $restart_min
     * @return string
     */
    public function system_restart($fd, $mac, $restart_min = 0): string
    {
        try {
            $b = new BKV();
            $b->addByNumberKey(0x01, 0x100d, 2);
            $b->addByNumberKey(0x02, $this->generateRequestId());
            $b->addByNumberKey(0x03, $b->handleMacId($mac), 8);
            $b->addByNumberKey(0x1f, (int)$restart_min, 1);
            $result = $b->pack();
            $message = $this->blService::buildFrame($b->bufferToHex($result));
            $data['fd'] = $fd;
            $data['message'] = $message;
            $this->post_curl('http://123.156.230.117:9005/tcp/query_device',$data);
            return $message;
        } catch (\Exception $e) {
            Normal::logger('system_restart-send--message---error---' . $e->getMessage() . $e->getTraceAsString(), 'notify-event');
            return $e->getMessage();
        }
    }


    public function generateRequestId(): string
    {
        return random_bytes(8);
    }

    public static function buildFrame(string $bkvData): string
    {
        // 1. 计算帧长（帧长字段 + 数据域 + 校验和字段）
        $bkvData = hex2bin($bkvData);
        $frameLength = strlen($bkvData) + 3; // 2 字节帧长 + 1 字节校验和
        $frameLengthHex = pack('n', $frameLength); // 将帧长转换为 2 字节二进制

        // 2. 计算校验和（累加校验和）
        $checksum = self::calculateChecksum($frameLengthHex . $bkvData);
        $checksumHex = pack('C', $checksum); // 将校验和转换为 1 字节二进制

        // 3. 拼接完整帧
        $frame = hex2bin('fcff') // 帧头（平台下行）
            . $frameLengthHex // 帧长
            . $bkvData // 数据域（BKV 数据）
            . $checksumHex // 校验和
            . hex2bin('fcee'); //
        return $frame;
    }

    private static function calculateChecksum(string $data): int
    {
        $checksum = 0;
        for ($i = 0; $i < strlen($data); $i++) {
            $checksum += ord($data[$i]);
        }
        return $checksum & 0xFF; // 取低 8 位
    }

}
